<template>
  <div class="wrapper">
    <div class="btn btn-mg" @click="startGame">新游戏</div>
    <div>
      <div class="over" v-if="isGameOver">
        <p>游戏结束!</p>
        <div class="btn" @click="startGame">重新开始</div>
      </div>
      <div class="box">
        <div class="row" v-for="row in cellArr">
          <div class="col" :class="'cell-' + col" v-for="col in row">
            {{ col }}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useThrottleFn } from '@vueuse/core';

const cellArr = ref<any[][]>([]);
const isGameOver = ref(false);
const xDown = ref(null);
const yDown = ref(null);

const startGame = () => {
  isGameOver.value = false;
  cellArr.value = Array.from(Array(4)).map(() => Array(4).fill(0));
  fillOneEmptyCell();
  fillOneEmptyCell();
};
//找到可以用于生成新数字的单元格，并生成新的数字进行填充
const fillOneEmptyCell = () => {
  let cellIndex = findOneEmptyCell();
  console.log(cellIndex);
  if (cellIndex != -1) {
    let row = Math.floor(cellIndex / 4);
    let col = cellIndex % 4;
    cellArr.value[row][col] = getRandomValue();
  }
};
//生成新的数字，90%几率生成2，10%几率生成4
const getRandomValue = () => {
  return random(1) < 0.9 ? 2 : 4;
};
//生成随机数，0=<结果<max
const random = (max: number) => {
  return Math.floor(Math.random() * max);
};
//找到可以用于生成新数字的单元格
const findOneEmptyCell = () => {
  let cells = [];
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      if (!cellArr.value[i][j]) {
        cells.push(i * 4 + j);
      }
    }
  }

  if (cells.length) {
    return cells[random(cells.length)];
  } else {
    return -1;
  }
};
const handleTouchStart = (evt: any) => {
  console.log('handleTouchStart');
  xDown.value = evt.touches[0].clientX;
  yDown.value = evt.touches[0].clientY;
};
const handleTouchMove = (evt: any) => {
  console.log('handleTouchMove');
  if (!xDown.value || !yDown.value) {
    return;
  }
  var xUp = evt.touches[0].clientX;
  var yUp = evt.touches[0].clientY;

  var xDiff = xDown.value - xUp;
  var yDiff = yDown.value - yUp;
  if (Math.abs(xDiff) > Math.abs(yDiff)) {
    if (xDiff > 0) {
      moveLeft();
    } else {
      moveRight();
    }
  } else {
    if (yDiff > 0) {
      moveUp();
    } else {
      moveDown();
    }
  }
};
onMounted(() => {
  startGame();
  document.addEventListener('keyup', keyDown);
  document.addEventListener('touchstart', handleTouchStart, false);
  document.addEventListener(
    'touchmove',
    useThrottleFn(handleTouchMove, 500),
    true,
  );
});

/*
判断是否还可以移动。
1、当前单元格是否为0；
2、当前单元格和右侧单元格是否相等；
3、当前单元格和下方单元格是否相等。
  */
const canMove = () => {
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      let cell = cellArr.value[i][j];
      if (cell) {
        //和右侧单元格比较，是否相等
        if (j < 3 && cellArr.value[i][j] == cellArr.value[i][j + 1]) {
          return true;
        }
        //和下方单元格比较，是否相等
        if (i < 3 && cellArr.value[i][j] == cellArr.value[i + 1][j]) {
          return true;
        }
      } else {
        return true;
      }
    }
  }
  return false;
};
/**
 * 将单元格数向左或向右移动，移除0并对相邻相同数进行叠加
 * toLeft表示是否是向左
 */
const horizontalMoveCells = (toLeft: boolean) => {
  for (let i = 0; i < 4; i++) {
    let newArr = Array(4).fill(0);
    for (let j = 0; j < 4; j++) {
      newArr[j] = cellArr.value[i][j];
    }
    let resultArr = removeZerosAndAdd(newArr, toLeft);
    for (let j = 0; j < 4; j++) {
      cellArr.value[i][j] = resultArr[j];
    }
  }
};
/**
 * 将单元格数向下或向上移动，移除0并对相邻相同数进行叠加
 * toUp表示是否是向上
 */
const verticalMoveCells = (toUp: boolean) => {
  for (let i = 0; i < 4; i++) {
    let newArr = Array(4).fill(0);
    for (let j = 0; j < 4; j++) {
      newArr[j] = cellArr.value[j][i];
    }
    let resultArr = removeZerosAndAdd(newArr, toUp);
    for (let j = 0; j < 4; j++) {
      cellArr.value[j][i] = resultArr[j];
    }
  }
};
/**
 * 1、去掉数组中的0，向头或向尾压缩数组。
 * 0,4,0,4向左压缩变成：4,4,0,0. 向右压缩变成：0,0,4,4
 * 2、相邻的数如果相同，则进行相加运算。
 * 4,4,0,0向左叠加变成：8,0,0,0. 向右叠加变成：0,0,0,8
 * toHead表示是否是头压缩
 */
const removeZerosAndAdd = (arr: number[], toHead: boolean) => {
  let newArr = Array(4).fill(0);
  let arrWithoutZero = arr.filter((x) => x !== 0); //去掉所有的0
  if (arrWithoutZero.length == 0) {
    return newArr;
  }
  if (toHead) {
    for (let i = 0; i < arrWithoutZero.length; i++) {
      newArr[i] = arrWithoutZero[i];
    }
    //对相邻相同的数进行叠加
    for (let i = 0; i < newArr.length - 1; i++) {
      if (newArr[3 - i] === newArr[2 - i] && newArr[3 - i] !== 0) {
        newArr[3 - i] = 0;
        newArr[2 - i] *= 2;
      }
    }
  } else {
    for (let i = 0; i < arrWithoutZero.length; i++) {
      newArr[newArr.length - i - 1] =
        arrWithoutZero[arrWithoutZero.length - i - 1];
    }

    //对相邻相同的数进行叠加
    for (let i = 0; i < newArr.length - 1; i++) {
      if (newArr[i] === newArr[i + 1] && newArr[i] !== 0) {
        newArr[i] = 0;
        newArr[i + 1] *= 2;
      }
    }
  }

  return newArr;
};
//键盘监听事件
const keyDown = (e: any) => {
  switch (e.keyCode) {
    case 38: //上
      moveUp();
      break;
    case 40: //下
      moveDown();
      break;
    case 37: //左
      moveLeft();
      break;
    case 39: //右
      moveRight();
      break;
  }
};
const moveUp = () => {
  verticalMoveCells(true);
  checkGameOverOrContinue();
};
const moveDown = () => {
  verticalMoveCells(false);
  checkGameOverOrContinue();
};
const moveLeft = () => {
  horizontalMoveCells(true);
  checkGameOverOrContinue();
};
const moveRight = () => {
  horizontalMoveCells(false);
  checkGameOverOrContinue();
};
const checkGameOverOrContinue = () => {
  if (canMove()) {
    fillOneEmptyCell();
  } else {
    isGameOver.value = true;
  }
};
</script>

<style scoped lang="scss">
.wrapper {
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
  padding-top: 50px;
  .over {
    position: absolute;
    width: 400px;
    height: 400px;
    background: rgba(238, 228, 218, 0.73);
    z-index: 1000;
    border-radius: 5px;
    text-align: center;
    color: #8f7a66;
    p {
      font-size: 60px;
      font-weight: bold;
      height: 60px;
      line-height: 60px;
    }
  }
  .btn {
    display: inline-block;
    padding: 0 20px;
    height: 40px;
    line-height: 40px;
    border-radius: 5px;
    cursor: pointer;
    text-align: center;
    color: #f9f6f2;
    background: #8f7a66;
    &.btn-mg {
      margin-bottom: 10px;
    }
  }
  .box {
    width: 400px;
    height: 400px;
    padding: 15px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    box-sizing: border-box;
    border-radius: 5px;
    background: #bbada0;
    .row {
      width: 100%;
      height: 23%;
      display: flex;
      flex-direction: row;
      justify-content: space-between;
      .col {
        width: 23%;
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 24px;
        border-radius: 3px;
        background: #cec1b3;
        &.cell-0 {
          color: #00000000;
        }
        &.cell-2 {
          background: #eee4da;
        }
        &.cell-4 {
          background: #ede0c8;
        }
        &.cell-8 {
          background: #f26179;
        }
        &.cell-16 {
          background: #f59563;
        }
        &.cell-32 {
          background: #f67c5f;
        }
        &.cell-64 {
          background: #f65e36;
        }
        &.cell-128 {
          background: #edcf72;
        }
        &.cell-256 {
          background: #edcc61;
        }
        &.cell-512 {
          background: #90c000;
        }
        &.cell-1024 {
          background: #3365a5;
        }
        &.cell-2048 {
          background: #90c000;
        }
        &.cell-4096 {
          background: #60b0c0;
        }
        &.cell-8192 {
          background: #9030c0;
        }
      }
    }
  }
}
</style>
